package main

import (
	"fmt"
	"reflect"
)

func reflectTest(b interface{}) {
	typef := reflect.TypeOf(b)
	//type: int
	fmt.Println("type:", typef)

	value := reflect.ValueOf(b)

	//value: 1
	fmt.Println("value:", value)
	//value:1,type:reflect.Value
	fmt.Printf("value:%v,type:%T\n", value, value)
	var realVal interface{}

	switch b.(type) {
	case int:
		realVal = value.Int()
	case string:
		realVal = value.String()
	case bool:
		realVal = value.Bool()
	default:
		realVal = value.Interface()
	}
	//realVal:1
	fmt.Printf("realVal:%v\n", realVal)

	//获取Value 的接口
	iv := value.Interface()
	//通过断言，获取其原本的值，即 传进来的参数arg
	rawVal := iv.(int)
	//rawValue:1
	fmt.Printf("rawValue:%v", rawVal)

}
func reflectStruct(b interface{}) {
	typef := reflect.TypeOf(b)
	//type:main.People
	fmt.Printf("type:%v\n", typef)

	value := reflect.ValueOf(b)
	//value:{haha 22}
	fmt.Printf("value:%v\n", value)

	inter := value.Interface()
	// 类型断言转为原类型
	people := inter.(People)
	//structVal:{haha 22}
	fmt.Printf("structVal:%v\n", people)
	//age: 22 name: haha
	fmt.Println("age:", people.Age, "name:", people.Name)

	//通过字段位置获取对应字段 返回的也是 Value 类型
	field0 := value.Field(0)

	//因为我们知道 People 这个字段的类型，因此直接通过 String()获取
	//field0:haha
	fmt.Printf("field0:%v\n", field0.String())
	//field0:22
	fmt.Printf("field0:%v\n", value.Field(1).Int())

	//通过字段名获取对应的字段，返回 Value 类型
	//FieldByName(name):haha
	fmt.Printf("FieldByName(name):%v\n", value.FieldByName("Name"))
	//FieldByName(age):22
	fmt.Printf("FieldByName(age):%v\n", value.FieldByName("Age"))
	fmt.Println("字段数量:", value.NumField())
	fmt.Println("可导出的方法数量:", value.NumMethod())

	valueKind := value.Kind()
	typeKind := typef.Kind()
	//value.Kind():struct,type.Kind():struct
	fmt.Printf("value.Kind():%v,type.Kind():%v", valueKind, typeKind)

}

// b 必须传指针才能修改原值
func SetVal(b interface{}) {
	value := reflect.ValueOf(b)
	elem := value.Elem()
	switch b.(type) {
	case *int:
		elem.SetInt(1)
	case *string:
		elem.SetString("1")

	}

}

//通过反射获取并修改结构体的值
func modifyStructVal(b interface{}) {
	typef := reflect.TypeOf(b)
	value := reflect.ValueOf(b)
	kind := value.Kind()
	if kind != reflect.Ptr {
		//值处理结构体
		fmt.Println("非结构体")
		return
	}
	elem := value.Elem()
	//字段数
	numField := elem.NumField()
	fmt.Println("字段数:", numField)
	numMethod := elem.NumMethod()
	fmt.Println("方法数:", numMethod)
	//遍历字段 【字段排序按照结构体内定义顺序排序】
	for i := 0; i < numField; i++ {
		field := elem.Field(i)
		fmt.Printf("第%v 个字段值:%v\t", i, field)
		//通过 Type 拿到的 Filed 是一个StructField结构体
		structField := typef.Elem().Field(i)
		//获取该字段的 tag
		fmt.Println("字段名:", structField.Name, ",tag:", structField.Tag.Get("json"))
	}
	//获取所有的方法【方法排序按照方法名进行排序】
	for i := 0; i < numMethod; i++ {
		//返回 Method 对象
		method := typef.Elem().Method(i)
		//第0 个方法:GetName      Type:func(main.People) string   PkgPath:
		fmt.Printf("第%v 个方法:%v\t", i, method.Name)
		fmt.Printf("Type:%v\n", method.Type)
	}
	//通过方法名获取方法
	printPeopleM := elem.MethodByName("PrintPeople")
	fmt.Println("调用PrintPeople方法:")
	_ = printPeopleM.Call(nil)

	//GetName方法获取People.Name
	getNameM := elem.MethodByName("GetName")
	//返回的是 Value 切片，按照返回结果数量依次放到切片中
	res := getNameM.Call(nil)
	fmt.Println("GetName resp:", res[0].String())

	//SetArg【注意这个方法是指针对象才能调用，因此直接使用 value 调用】
	setArgM := value.MethodByName("SetArg")
	_ = setArgM.Call([]reflect.Value{
		//第一个参数【name】
		reflect.ValueOf("哈哈"),
		//第二个参数【age】
		reflect.ValueOf(23),
	})
	fmt.Println("调用SetArg后，各字段的值:")
	printFiledVal(numField, elem)

	//手动再修改字段的值
	elem.FieldByName("Name").SetString("嘻嘻")
	elem.FieldByName("Age").SetInt(25)
	fmt.Println("手动修改对应字段值后，各字段的值:")

	printFiledVal(numField, elem)

}

func printFiledVal(numField int, elem reflect.Value) {
	for i := 0; i < numField; i++ {
		field := elem.Field(i)
		fmt.Printf("第%v 个字段值:%v\t", i, field)
	}
	fmt.Println()
}
func newStruct(b interface{}) {
	typef := reflect.TypeOf(b)
	fmt.Println(typef)
	//如果是值类型就不同.Elem()
	// peoplePtrValue := reflect.New(typef)
	//返回的是一个 Value 类型，持有类型为 typef 的新申请的零值的指针
	peoplePtrValue := reflect.New(typef.Elem())
	people := peoplePtrValue.Interface().(*People)
	fmt.Println(people)
}
func main() {
	//reflectStruct(People{
	//	Name: "haha",
	//	Age:  22,
	//})
	/*var num = 2
	SetVal(&num)
	fmt.Println("修改后:", num)*/
	p := People{
		Name: "haha",
		Age:  22,
	}
	newStruct(&p)
	fmt.Println("调用反射后，p的值:")
	fmt.Println(p)
}

type People struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func (p People) PrintPeople() {
	fmt.Println("name:", p.Name, "age:", p.Age)
}
func (p People) GetName() string {
	return p.Name
}
func (p *People) SetArg(name string, age int) {
	p.Name = name
	p.Age = age
}
